/**
 * ===========================================================================
 * 版权所有 湖南免了么网络科技有限公司，并保留所有权利。
 * 网站地址: http://www.mianle.me
 * ===========================================================================
 * 16:14 2018/6/9
 */
package com.mianle.userapi.service;

import com.mianle.userapi.exception.CustomizeException;
import com.mianle.userapi.util.GeoExtendResult;
import com.mianle.userapi.util.SearchGeo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.core.BoundGeoOperations;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author: yiyuan
 * @Description:
 * @Date: Created in 16:14 2018/6/9
 *
 */
@Service
@Slf4j
public class RedisGeo {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 前缀
     */
    public static final String KEY_PREFIX_GEO = "geo:";

    public int saveRedisKey() throws CustomizeException{
        try {
            if(true) {
                throw new CustomizeException("测试异常");
            }
            return 1;
        } catch (CustomizeException e) {
            throw e;
        } catch (Exception e) {
            throw e;
        }
    }

    public void test(String key, Double x, Double y, Double distance) {
        BoundGeoOperations boundGeoOperations = redisTemplate.boundGeoOps(KEY_PREFIX_GEO + key);
        //南京市 118.803805,32.060168
        //Point nanjing = new Point(118.803805,32.060168);
        //当redis里面已经存在了相同的member的时候，则仅仅更新经纬度

        //geoadd：增加某个地理位置的坐标
        //System.out.println(boundGeoOperations.geoAdd(nanjing, "南京市"));

        //Point beijing = new Point(116.397039,39.9077);
        //System.out.println(boundGeoOperations.geoAdd(beijing, "北京市"));

        //geodist：获取两个地理位置的距离
        //Distance distance = boundGeoOperations.geoDist("南京市", "北京市", Metrics.KILOMETERS);
        //System.out.println("南京市到北京市之间的距离是：" + distance.getValue() + "km");

        //geohash：获取某个地理位置的geohash值
        //List<String> list = boundGeoOperations.geoHash("南京市");
        //System.out.println("南京市的geoHash = " + list.get(0));

        //geopos：获取某个地理位置的坐标
        //List<Point> pointList = boundGeoOperations.geoPos("南京市");
        //System.out.println("南京市的经纬度为 = " + pointList.get(0));

        //georadius：根据给定地理位置坐标获取指定范围内的地理位置集合
        //查询南京市1000KM范围内的城市
        //Circle within = new Circle(new Point(x, y), new Distance(distance, RedisGeoCommands.DistanceUnit.KILOMETERS));
        Circle within = new Circle(new Point(x, y), 1000000);
        //设置geo查询参数
        RedisGeoCommands.GeoRadiusCommandArgs geoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        //查询返回结果包括距离和坐标
        geoRadiusArgs = geoRadiusArgs.includeCoordinates().includeDistance();
        //按查询出的坐标距离中心坐标的距离进行排序
        geoRadiusArgs.sortAscending();
        //限制查询返回的数量
        geoRadiusArgs.limit(10);
        GeoResults<RedisGeoCommands.GeoLocation<String>> geoResults = boundGeoOperations.geoRadius(within, geoRadiusArgs);
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> geoResultList = geoResults.getContent();
        for (GeoResult geoResult : geoResultList) {
            System.out.println("geoRadius  " + geoResult.getContent());
        }

        //georadiusbymember：根据给定地理位置获取指定范围内的地理位置集合
//        geoRadiusArgs.limit(1);
//        geoResults = boundGeoOperations.geoRadiusByMember("南京市",new Distance(1000000), geoRadiusArgs);
//        geoResultList = geoResults.getContent();
//        for (GeoResult geoResult : geoResultList) {
//            System.out.println("geoRadiusByMember  " + geoResult.getContent());
//        }

        //删除位置信息,此命令不是geo提供的，是使用zrem命令删除的
        //System.out.println(boundGeoOperations.geoRemove("南京市"));
    }


    /**
     * 通过给定的坐标和距离(km)获取范围类其它的坐标信息
     * 以给定的经纬度为中心， 找出某一半径内的元素
     * @return
     */
    public List<? extends GeoResult<RedisGeoCommands.GeoLocation<String>>> radiusGeo(SearchGeo searchGeo) {
        try {
            if(searchGeo.getKey() == null) {
                return null;
            }
            String k = KEY_PREFIX_GEO + searchGeo.getKey();
            GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
            //设置geo查询参数
            RedisGeoCommands.GeoRadiusCommandArgs geoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
            //查询返回结果包括距离和坐标
            geoRadiusArgs = geoRadiusArgs.includeCoordinates().includeDistance();
            //按查询出的坐标距离中心坐标的距离进行排序
            if(searchGeo.getSort() == null) {
                geoRadiusArgs.sortAscending();
            } else {
                if (Direction.ASC.equals(searchGeo.getSort())) {
                    geoRadiusArgs.sortAscending();
                } else if (Direction.DESC.equals(searchGeo.getSort())) {
                    geoRadiusArgs.sortDescending();
                }
            }
            //限制查询数量
            geoRadiusArgs.limit(searchGeo.getLimit() == null ? 100 : searchGeo.getLimit());
            GeoResults<GeoLocation<String>> radiusGeo = geoOps.geoRadius(k, new Circle(new Point(searchGeo.getLng(), searchGeo.getLat()),
                    new Distance(searchGeo.getDistance(), searchGeo.getDistanceUnit())), geoRadiusArgs);

            //List<GeoResult<RedisGeoCommands.GeoLocation<String>>> geoResultList = radiusGeo.getContent();

            List<? extends GeoResult<RedisGeoCommands.GeoLocation<String>>> res = radiusGeo.getContent();
            List<GeoExtendResult<GeoLocation<String>>> result = (List<GeoExtendResult<GeoLocation<String>>>) res;
            return result;

        } catch (Throwable t) {
            log.error("通过坐标[{}, {} ]获取范围[ {} km的其它坐标失败]" + ", error[{}]", searchGeo.getLng(), searchGeo.getLat(), searchGeo.getDistance(), t.getMessage());
        }
        return null;
    }


    /**
     *
     * @MethodName：cacheGeo
     * @param k
     * @param x
     * @param y
     * @param member
     * @param time(单位秒)  <=0 不过期
     * @return
     * @ReturnType：boolean
     * @Description：缓存地理位置信息
     * @Creator：chenchuanliang
     * @CreateTime：2017年5月18日上午11:30:23
     * @Modifier：
     * @ModifyTime：
     */
    public boolean cacheGeo(String k, double x, double y, String member, long time) {
        String key = KEY_PREFIX_GEO + k;
        try {
            GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
            Long r = geoOps.geoAdd(key, new Point(y, x) , member);
            if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
        } catch (Throwable t) {
            log.error("缓存[" + key +"]" + "失败, point["+ x + "," +
                    y + "], member[" + member + "]" +", error[" + t + "]");
        }
        return true;
    }

    /**
     *
     * @MethodName：cacheGeo
     * @param k
     * @param locations
     * @param time(单位秒)  <=0 不过期
     * @return
     * @ReturnType：boolean
     * @Description：
     * @Creator：chenchuanliang
     * @CreateTime：2017年5月18日上午11:31:33
     * @Modifier：
     * @ModifyTime：
     */
    public boolean cacheGeo(String k, Iterable<GeoLocation<String>> locations, long time) {
        try {
            for (GeoLocation<String> location : locations) {
                cacheGeo(k, location.getPoint().getX(), location.getPoint().getY(), location.getName(), time);
            }
        } catch (Throwable t) {
            log.error("缓存[" + KEY_PREFIX_GEO + k + "]" + "失败" +", error[" + t + "]");
        }
        return true;
    }

    /**
     *
     * @MethodName：removeGeo
     * @param k
     * @param members
     * @return
     * @ReturnType：boolean
     * @Description：移除地理位置信息
     * @Creator：chenchuanliang
     * @CreateTime：2017年5月18日上午10:53:01
     * @Modifier：
     * @ModifyTime：
     */
    public boolean removeGeo(String k, String...members) {
        String key = KEY_PREFIX_GEO + k;
        try {
            GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
            geoOps.geoRemove(key, members);
        } catch (Throwable t) {
            log.error("移除[" + key +"]" + "失败" +", error[" + t + "]");
        }
        return true;
    }

    /**
     *
     * @MethodName：distanceGeo
     * @param k
     * @param member1
     * @param member2
     * @return Distance
     * @ReturnType：Distance
     * @Description：根据两个成员计算两个成员之间距离
     * @Creator：chenchuanliang
     * @CreateTime：2017年5月18日上午10:58:33
     * @Modifier：
     * @ModifyTime：
     */
    public Distance distanceGeo(String k, String member1, String member2) {
        String key = KEY_PREFIX_GEO + k;
        try {
            GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
            return geoOps.geoDist(key, member1, member2);
        } catch (Throwable t) {
            log.error("计算距离[" + key +"]" + "失败, member[" + member1 + "," + member2 +"], error[" + t + "]");
        }
        return null;
    }

    /**
     *
     * @MethodName：getGeo
     * @param k
     * @param members
     * @return
     * @ReturnType：List<Point>
     * @Description：根据key和member获取这些member的坐标信息
     * @Creator：chenchuanliang
     * @CreateTime：2017年5月18日上午11:02:01
     * @Modifier：
     * @ModifyTime：
     */
    public List<Point> getGeo(String k, String...members) {
        String key = KEY_PREFIX_GEO + k;
        try {
            GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
            return geoOps.geoPos(key, members);
        } catch (Throwable t) {
            log.error("获取坐标[" + key +"]" + "失败]" + ", error[" + t + "]");
        }
        return null;
    }


    //添加附近的人
    public boolean geoadd(String key,double longitude,double latitude){
        boolean flag = false;
        try{
            redisTemplate.boundGeoOps("nearPeople").geoAdd(new Point(longitude,latitude),key);
            flag = true;
        }catch (Exception e){
            e.printStackTrace();
        }

        return flag;
    }
    //查询附近的人
    public List geoQuery(double longitude,double latitude) {
        List list = null;
        try{
            list = redisTemplate.opsForGeo().geoRadius("nearPeople", new Circle(new Point(longitude, latitude),
                            new Distance(500, Metrics.KILOMETERS)),
                    RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates()).getContent();

        }catch (Exception e){
            e.printStackTrace();
        }

        return list;
    }


    //set值
    public void set(String key, String value) {
        try {
            redisTemplate.opsForValue().set(key,value);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //get值
    public String get(String key) {

        try {
            return ""+redisTemplate.opsForValue().get(key);
        }catch (Exception e){
            e.printStackTrace();
            return "";
        }
    }
    //自增1
    public void incr(String key) {
        int num = Integer.parseInt(""+redisTemplate.opsForValue().get(key));
        num=num+1;
        redisTemplate.opsForValue().set(key,num);
    }
    //到期时间
    public boolean expire(String key, int second) {
        boolean result = redisTemplate.expire(key,second, TimeUnit.SECONDS);
        return result;
    }

    //删除
    public void del(String key) {
        redisTemplate.delete(key);
    }
    //是否存在key
    public boolean hasKey(String s) {
        Boolean flag =  redisTemplate.hasKey(s);
        return flag;

    }


/*    private void tests() {
        RedisConnection redisConnection = redisConnectionFactory.getConnection();
        Jedis jedis = (Jedis) redisConnection.getNativeConnection();

        System.out.println(jedis);
        jedis.geoadd("333",
                91.118970,
                29.654210,
                "dc");

        redisConnection.close();
    }*/

    /**
     * 通过反射从spring-boot-start-data-redis 获取jedis实例
     */
/*    public void test() {
        ////根据类和属性名称查找属性字段
        Field jedisField = ReflectionUtils.findField(JedisConnection.class, "jedis");
        //判断一个属性是否可以访问的方法
        ReflectionUtils.makeAccessible(jedisField);

        System.out.println(redisConnectionFactory.getConnection());
        //根据属性字段和对象，获取的对象中字段的值
        Jedis jedis = (Jedis) ReflectionUtils.getField(jedisField, redisConnectionFactory.getConnection());

        jedis.geoadd("2222",
                91.118970,
                29.654210,
                "df");
        //String result = jedis.set("test-key", "Hello world-", "NX", "EX", 1);
    }*/
    public static void main(String[] args) {
        Long totalSurplusCopy = NumberUtils.createBigDecimal(String.valueOf("10000000")).add(new BigDecimal("20")).longValue();
        System.out.println(totalSurplusCopy);
    }



}
